package doing;

import org.junit.Test;

import java.util.*;

import static com.study.util.LogUtil.info;

/**
 * 355. Design Twitter 设计推特
 * <p>
 * 设计一个简化版的推特(Twitter)，可以让用户实现发送推文，关注/取消关注其他用户，能够看见关注人（包括自己）的最近十条推文。你的设计需要支持以下的几个功能：
 * <p>
 * postTweet(userId, tweetId): 创建一条新的推文
 * getNewsFeed(userId): 检索最近的十条推文。每个推文都必须是由此用户关注的人或者是用户自己发出的。推文必须按照时间顺序由最近的开始排序。
 * follow(followerId, followeeId): 关注一个用户
 * unfollow(followerId, followeeId): 取消关注一个用户
 * <p>
 * 2019-05-18 12:41
 **/
@SuppressWarnings("all")
public class DesignTwitter {

    class Twitter {

        int timestamp;
        Map<Integer, List<int[]>> tweets;
        Map<Integer, Set<Integer>> followers;

        public Twitter() {
            timestamp = 0;
            tweets = new HashMap();
            followers = new HashMap();
        }

        public void postTweet(int userId, int tweetId) {
            timestamp++;

            List<int[]> list = tweets.get(userId);
            if (list == null) {
                list = new ArrayList<>();
            }

            list.add(new int[]{timestamp, tweetId});
            tweets.put(userId, list);

            //自己关注自己，以便后面返回信息流
            follow(userId, userId);

        }

        public List<Integer> getNewsFeed(int userId) {
            List<Integer> res = new ArrayList<>();

            //保存本人及关注者的所有tweet
            List<int[]> total = new ArrayList<>();

            //本人及关注者的集合
            Set<Integer> fs = followers.get(userId);
            if (fs != null) {
                for (int f : fs) {
                    List<int[]> temp = tweets.getOrDefault(f, new ArrayList<>());
                    total.addAll(temp);
                }
            }

            //将所有tweet放入大顶堆
            PriorityQueue<int[]> pq = getPq();
            for (int[] i : total) {
                pq.offer(i);
            }

            //返回10个最近的tweet
            for (int i = 0; i < 10 && !pq.isEmpty(); i++) {
                int[] tweet = pq.poll();
                res.add(tweet[1]);
            }
            return res;
        }

        public void follow(int followerId, int followeeId) {
            Set<Integer> followSet = followers.get(followerId);
            if (followSet == null) {
                followSet = new HashSet<>();
            }
            followSet.add(followeeId);
            followers.put(followerId, followSet);
        }

        public void unfollow(int followerId, int followeeId) {
            Set<Integer> followSet = followers.get(followerId);
            if (followerId == followeeId || followSet == null) {
                return;
            } else {
                followSet.remove(followeeId);
            }

        }

        //返回一个大顶堆
        private PriorityQueue<int[]> getPq() {
            return new PriorityQueue<>(new Comparator<int[]>() {
                public int compare(int[] n1, int[] n2) {
                    return n2[0] - n1[0];
                }
            });
        }
    }

    @Test
    public void test() {
        Twitter twitter = new Twitter();

        // 用户1发送了一条新推文 (用户id = 1, 推文id = 5).
        twitter.postTweet(1, 5);

        // 用户1的获取推文应当返回一个列表，其中包含一个id为5的推文.
        info(twitter.getNewsFeed(1));

        // 用户1关注了用户2.
        twitter.follow(1, 2);

        // 用户2发送了一个新推文 (推文id = 6).
        twitter.postTweet(2, 6);

        // 用户1的获取推文应当返回一个列表，其中包含两个推文，id分别为 -> [6, 5].
        // 推文id6应当在推文id5之前，因为它是在5之后发送的.
        info(twitter.getNewsFeed(1));

        // 用户1取消关注了用户2.
        twitter.unfollow(1, 2);

        // 用户1的获取推文应当返回一个列表，其中包含一个id为5的推文.
        // 因为用户1已经不再关注用户2.
        info(twitter.getNewsFeed(1));
    }
}